home *** CD-ROM | disk | FTP | other *** search
/ Power Programmierung / Power-Programmierung (Tewi)(1994).iso / magazine / pctchnqs / 1990 / number5 / com_pp.asm < prev    next >
Assembly Source File  |  1990-09-02  |  39KB  |  1,069 lines

  1. title   COM_PP.ASM
  2.  
  3. ;**************************************************************************
  4. ;*      COM_PP.ASM is a set of RS232 communications functions. These func-
  5. ;*  provide basic, interrupt-driven serial communications through an 8052
  6. ;*  UART. The com_open() and com_set_buffers() functions MUST be called
  7. ;*  before the com_read() or com_write() functions.
  8. ;*
  9.  
  10. .model small
  11.  
  12. if @codesize                                ; large or compact model
  13.     bp_ofs  equ 6
  14. else                                        ; small or medium model
  15.     bp_ofs  equ 4
  16. endif
  17.  
  18.  
  19. ;** Constant Definitions ***************
  20. ERROR       equ     -1
  21. OK          equ     0
  22.  
  23. ;* com port status report bits
  24. PORT_OPEN   equ     0001h
  25. RECVD_CHAR  equ     0002h
  26. SNDNG_PCKT  equ     0004h
  27. HAND_SHAKING    equ 0008h
  28. OVERRUN_ERR equ     0010h
  29. PARITY_ERR  equ     0020h
  30. FRAMING_ERR equ     0040h
  31. BREAK_ERR   equ     0080h
  32. OVERFLOW    equ     0100h
  33. INVALID_PORT    equ 0200h
  34. PORT_NOT_FND    equ 0400h
  35.  
  36. ;* hand-shaking function codes
  37. RECV_TEST   equ     1
  38. SEND_TEST   equ     2
  39. BUF_EMPTY   equ     3
  40. ;* line status bits - used to test Line Status Register
  41. OVERRUN     equ     02h
  42. PARITY      equ     04h
  43. FRAMING     equ     08h
  44. BREAK       equ     10h
  45.  
  46. ;* interrupt id register bits
  47. DATA_AVAILABLE  equ     04h
  48. TRANS_EMPTY     equ     02h
  49.  
  50. ;* interrupt enable bits
  51. DATA_AVAIL_INT  equ     01h                 ; Enable data available int
  52. TRANS_EMPTY_INT equ     02h                 ; Enable transmitter empty int
  53. LINE_STATUS_INT equ     04h                 ; Enable Line Status Reg int
  54. MODEM_STAT_INT  equ     08h                 ; Enable Modem Status reg int
  55.  
  56. ;* modem control bits
  57. DTR         equ     01h
  58. RTS         equ     02h
  59. OUT1        equ     04h
  60. OUT2        equ     08h
  61.  
  62. ;* Programmable Interrupt Controller (PIC)
  63. PIC_MASK_REG equ     21h
  64. PIC_EOI     equ     20h                     ; End Of Interrupt
  65.  
  66. ;* com port addresses
  67. COM1        equ     3f8h
  68. COM2        equ     2f8h
  69.  
  70. ;* these are offsets from the base com port address to its various control
  71. ;* and status registers
  72. INT_ENABLE  equ     01h                     ; Interrupt Enable Register
  73. INT_ID      equ     02h                     ; Interrupt Identification Reg.
  74. MODEM_CONTROL   equ     04h                 ; Modem Control Register
  75. LINE_STATUS     equ     05h                 ; Line Status Register
  76. MODEM_STATUS    equ     06h                 ; Modem Status Register
  77.  
  78. ;******************************************************************************
  79. .data
  80.      public com_status, send_len, send_tail, send_buffer
  81.      public recv_len, recv_tail, recv_buffer, old_stack, stack_top
  82.      public int_enable_mask
  83.  
  84. com_status      dw  0                       ; local status word
  85. com_port        dw  0                       ; base address of com port
  86. com_int         db  0                       ; com interrupt vector
  87. PIC_mask        db  0                       ; Programmable Interrupt
  88.                                             ;    Controller mask
  89. int_enable_mask db  0                       ; this is the uart interrupt
  90.                                             ;   mask
  91. send_len        dw  0                       ; size of send buffer
  92. send_tail       dw  0                       ; send buffer tail
  93. send_buffer     dd  0                       ; pointer to private send buffer
  94. msg_len         dw  0                       ; length of send message
  95.  
  96. recv_len        dw  0                       ; size of receive buffer
  97. recv_tail       dw  0                       ; receive buffer tail
  98. recv_buffer     dd  0                       ; pointer to private receive buffer
  99.  
  100. old_vector      dd  0                       ; orginal com interrupt vector
  101. hand_shake      dd  0                       ; address of handshake function
  102. local_stack     dw  100h dup(0)
  103. stack_top       dw  0
  104.  
  105. ;******************************************************************************
  106. .code
  107.     public  _com_open, _com_write, _com_read, _com_close, _com_start_sending
  108.     public  _com_stop_sending, _com_get_status, _com_clr_recv_buf
  109.     public  _com_clr_send_buf, _com_set_handshake, _com_set_buffers
  110.     public  _com_chars_recvd, _com_chars_sent, _com_read_char,
  111.     public  _com_write_char
  112.  
  113. old_stack       dd  0                       ; stack of interrupted routine
  114. save_ax         dw  0
  115. ;**************************************************************************
  116. ;*      ASYNC_ISR is the interrupt service routine.  It is invoked anytime
  117. ;*  a byte is received by the UART (8250), or if the 8250 is ready to send
  118. ;*  another character, or if a line error is detected. The Interrupt
  119. ;*  Identification Register (INT_ID) is read to determine the interrupt type
  120. ;*  - Receive, Transmit, or Error - and the appropriate sub-routine is
  121. ;*  executed. Since the 8250 prioritizes simultaneous interrupts, the INT_ID
  122. ;*  is checked again prior to exiting to be sure all pending interrupts have
  123. ;*  been processed.
  124. ;*
  125. async_isr proc far
  126.  
  127. ; first save the interrupted routine's stack and substitute our local stack
  128. ; old_stack is in the code segment because at this point that's all we can
  129. ; count on
  130.     mov     word ptr cs:old_stack,sp
  131.     mov     word ptr cs:old_stack + 2,ss
  132.     mov     save_ax,ax
  133.     mov     ax,seg com_status
  134.     mov     ss,ax
  135.     mov     sp,offset stack_top
  136.     sti                                     ; re-enable interrupts
  137.  
  138.     push    bp
  139.     push    bx
  140.     push    cx
  141.     push    di
  142.     push    ds
  143.     push    dx
  144.     push    es
  145.     push    si
  146.  
  147.     mov     ax,seg com_status
  148.     mov     ds,ax
  149.     mov     dx,com_port                     ; get base address of com port
  150.     add     dx,INT_ID                       ; point to Int. Id. Register
  151.     in      al,dx
  152.  
  153. ai1:
  154. ; first check for a line status interrupt
  155.     cmp     al,6                            ; check for error interrupt
  156.     je      ai3
  157.     test    al,DATA_AVAILABLE               ; check for received character
  158.     jz      ai2                             ; if not, make next test
  159.                                             ; else
  160.     call    recv_char                       ; receive a character
  161.     jmp     ai_out
  162.  
  163. ai2:
  164. ; check for a transmit hold register empty interrupt
  165.     test    al,TRANS_EMPTY                  ; check for transmitter empty
  166.     jz      ai_out                          ; if not, leave
  167.                                             ; else
  168.     call    send_char                       ; send a character
  169.     jmp     ai_out
  170.  
  171. ai3:
  172. ; it was an error interrupt so identify it
  173.     mov     dx,com_port                     ; get port base address
  174.     add     dx,LINE_STATUS                  ; point to Line Status Register
  175.     in      al,dx                           ; read the register
  176.  
  177.     test    al,OVERRUN
  178.     jz      ai4
  179.     or      com_status,OVERRUN_ERR
  180. ai4:
  181.     test    al,PARITY
  182.     jz      ai5
  183.     or      com_status,PARITY_ERR
  184. ai5:
  185.     test    al,FRAMING
  186.     jz      ai6
  187.     or      com_status,FRAMING_ERR
  188. ai6:
  189.     test    al,BREAK
  190.     jz      ai_out
  191.     or      com_status,BREAK_ERR
  192.  
  193. ai_out:
  194. ; this is where we check for simultaneous interrupts
  195.     mov     dx,com_port                     ; get com port address
  196.     add     dx,INT_ID                       ; point to interrupt ident reg
  197.     in      al,dx                           ; read register
  198.     test    al,01h                          ; check for pending interrupt
  199.     jz      ai1                             ; and if so, process it
  200.  
  201. ; no pending interrupts
  202.     mov     dx,com_port                     ; re-set Int. Enable Reg.
  203.     add     dx,INT_ENABLE
  204.     mov     al,int_enable_mask
  205.     out     dx,al
  206.  
  207.     mov     al,PIC_EOI                      ; re-enable the 8259
  208.     out     20h,al
  209.  
  210.     pop     si
  211.     pop     es
  212.     pop     dx
  213.     pop     ds
  214.     pop     di
  215.     pop     cx
  216.     pop     bx
  217.     pop     bp
  218.  
  219. ; restore the stack
  220.     cli
  221.     mov     ss,word ptr cs:old_stack + 2
  222.     mov     sp,word ptr cs:old_stack
  223.     mov     ax,cs:save_ax
  224.     sti
  225.     iret
  226. async_isr endp
  227.  
  228.  
  229. ;* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  230. ;*      RECV_CHAR first inputs the character received and then checks
  231. ;*  for handshaking. If handshaking is enabled then the character
  232. ;*  received is passed to the hand-shaking routine. The hand-shaking
  233. ;*  function will clear AH if the byte passed to it should be saved
  234. ;*  otherwise AH will be non-zero.
  235. ;*      If byte is to be kept then a test for buffer overflow is made.
  236. ;*  If the buffer is full an error flag is set and the character is
  237. ;*  thrown away.
  238. ;*      Provided the above tests have been passed, the character is
  239. ;*  saved at the tail of the local receive buffer and a flag is set
  240. ;*  indicating that there are characters in the receive buffer.
  241. ;*
  242. recv_char proc near
  243.     mov     dx,com_port                     ; get com port address
  244.     in      al,dx                           ; & read character
  245.  
  246.     test    com_status,HAND_SHAKING         ; check for hand-shaking
  247.     jz      rc1                             ; no hand-shaking so continue
  248.                                             ; else
  249.     mov     ah,RECV_TEST                    ; set ah to function code
  250.     push    ax                              ; (al = character)
  251.     if @codesize                            ; large or compact model
  252.         call    far ptr [hand_shake]
  253.     else                                    ; small or medium model
  254.         call    word ptr [hand_shake]
  255.     endif
  256.     add     sp,2                            ; adjust stack
  257.     test    ah,0ffh                         ; see if ah is zero
  258.     jz      rc1                             ; if it is, continue
  259.                                             ; else
  260.     jmp     rc_out                          ; exit
  261.  
  262. rc1:
  263. ; make sure recv_tail is less than the receive buffer's length
  264.     mov     dx,recv_tail
  265.     cmp     dx,recv_len
  266.     jl      rc2
  267.     or      word ptr com_status,OVERFLOW
  268.     jmp     short rc_out
  269.  
  270. rc2:
  271. ; save the byte
  272.     push    es
  273.     les     di,recv_buffer                  ; es:di = receive buffer
  274.     add     di,recv_tail                    ; point to current end of buffer
  275.     mov     es:[di],al                      ; save character
  276.     inc     word ptr recv_tail              ; increment tail
  277.     or      com_status,RECVD_CHAR           ; and set receive character flag
  278.     pop     es
  279.  
  280. rc_out:
  281.     ret
  282. recv_char endp
  283.  
  284.  
  285. ;* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  286. ;*      SEND_CHAR first loads the next character to send into AL and then
  287. ;*  checks to see if hand-shaking is enabled. If so the hand-shaking
  288. ;*  function is called. The hand-shaking function will clear AH if the byte
  289. ;*  passed to it should be sent (and send_tail incremented) otherwise AH
  290. ;*  will be non-zero.
  291. ;*
  292. send_char proc near
  293. ; first get the next byte in the buffer
  294.     push    es
  295.     les     di,send_buffer
  296.     add     di,send_tail                    ; point to next character
  297.     mov     al,es:[di]                      ; move character into al
  298.     pop     es
  299.  
  300.     test    com_status,HAND_SHAKING         ; check protocol
  301.     jz      sc1
  302.     mov     ah,SEND_TEST                    ; set ah to function code
  303.     push    ax                              ; al = next send character
  304.     if @codesize                            ; large or compact model
  305.         call    far ptr [hand_shake]
  306.     else                                    ; small or medium model
  307.         call    word ptr [hand_shake]
  308.     endif
  309.     add     sp,2                            ; adjust stack
  310. ; if HAND_SHAKE returns with ah > 0 then it has replaced the character in
  311. ; al in which case send_tail is not changed, otherwise it is incremented.
  312. ; note that since the byte is not re-loaded from the buffer hand_shake
  313. ; can substitute another byte and still force send_tail to be incremented.
  314.     test    ah,0ffh
  315.     jnz     sc2
  316. sc1:
  317.     inc     send_tail
  318.  
  319. sc2:                                        ; loop until Trans Holding Reg
  320. ; this test shouldn't be necessary but at least one supposed 8250 clone
  321. ; (I don't recall whose) interrupts when the transmit shift register is
  322. ; empty and not the transmit hold register
  323.     mov     ah,al                           ; save send character in ah
  324.     mov     dx,com_port                     ; get port base address
  325.     add     dx,LINE_STATUS                  ; point to Line Status Register
  326. sc3:
  327.     in      al,dx
  328.     test    al,40h
  329.     jz      sc3
  330.  
  331.     sub     dx,LINE_STATUS                  ; point back to base address
  332.     mov     al,ah                           ; put character back in al
  333.     out     dx,al                           ; and send character
  334.  
  335.     mov     ax,msg_len                      ; get message length
  336.     cmp     ax,send_tail                    ; & compare to see if
  337.                                             ; we're at the end of the message
  338.     jne     short sc_out
  339.  
  340. ; we've sent the entire message so re-initialize the variables and turn
  341. ; off the transmit interrupt
  342.     mov     word ptr send_tail,0            ; reset send tail
  343.     mov     word ptr msg_len,0
  344.     and     com_status,not SNDNG_PCKT       ; turn sending flag off
  345.     and     int_enable_mask,not TRANS_EMPTY_INT ; reset interrupt status
  346.     mov     dx,com_port                     ; re-set Int. Enable Reg.
  347.     add     dx,INT_ENABLE                   ; point to Interrupt Enable Reg
  348.     mov     al,int_enable_mask              ; clear the transmit bit
  349.     out     dx,al
  350.  
  351. sc_out:
  352.     ret
  353. send_char endp
  354.  
  355. ;******************************************************************************
  356. ;*  int    com_open(int port,unsinged int params);
  357. ;*
  358. ;*      Only com1 and com2 are supported using IRQ4 and IRQ3 respectively.
  359. ;*  Once the port has been determined the BIOS (int 14h) is used to set
  360. ;*  it up. The current interrupt vector is saved for later restoration and
  361. ;*  ASYNC_ISR is substituted. DTR and RTS are pulled high. The UART's
  362. ;*  receive buffer is cleared, and interrupts are enabled on the UART and
  363. ;*  the 8259 Programmable Interrupt Controller (PIC).
  364. ;*
  365. ;*  Returns:    -1 if error, otherwise 0.
  366. ;*
  367. _com_open   proc
  368.     push    bp
  369.     mov     bp,sp
  370.     push    bx
  371.     push    ds
  372.     push    dx
  373.  
  374.     if @datasize                            ; large data model
  375.         mov     ax,seg com_status           ; change to local data segment
  376.         mov     ds,ax                       ;   if necessary
  377.     endif
  378.     test    com_status,PORT_OPEN            ; see if the port is already open
  379.     jz      co1                             ; if not, continue
  380.     jmp     co_err                          ; otherwise, return an error
  381. co1:
  382. ;* find out which com port - set up first for com2 and then if not
  383. ;* modify the settings
  384.     mov     PIC_mask,08h                    ; 8259 PIC mask for IRQ3
  385.     mov     com_int,0bh                     ; set interrupt to com2
  386.     mov     bx,0402h                        ; set si to BIOS com2 address
  387.     mov     dx,[bp + bp_ofs]                ; get com port
  388.     cmp     dx,1                            ; check for com2
  389.     je      co2                             ; and if true test port
  390.                                             ; else
  391.     mov     PIC_mask,10h                    ; 8259 PIC mask for IRQ4
  392.     inc     com_int                         ; reset interrupt to com1
  393.     sub     bx,2                            ; set si to BIOS com1 address
  394.     cmp     dx,0                            ; & check for com1
  395.     je      co2
  396.     or      com_status,INVALID_PORT
  397.     jmp     co_err                          ; only com1 & com2 are supported
  398.  
  399. co2:
  400.     mov     ax,0
  401.     push    ds
  402.     mov     ds,ax                           ; set ds to bios segment
  403.     mov     ax,[bx]                         ; get port address from BIOS area
  404.     pop     ds
  405.     cmp     ax,0                            ; if BIOS address is not 0 then
  406.     jne     co3                             ; initialize port
  407.                                             ; else
  408.     or      com_status,PORT_NOT_FND
  409.     jmp     co_err                          ; return an error
  410.  
  411. co3:
  412.     mov     com_port,ax                     ; save com port address
  413.     mov     ax,[bp + bp_ofs + 2]            ; get com parameters
  414.     int     14h                             ; call bios to set com port
  415.  
  416. ;* now we're ready to save the existing com interrupt vector and
  417. ;* substitute async_isr for it
  418.     push    es
  419.     mov     al,com_int                      ; vector address
  420.     mov     ah,35h                          ; DOS Get Vector function
  421.     int     21h                             ; call DOS
  422.     mov     word ptr old_vector,bx          ; and save the old
  423.     mov     word ptr old_vector + 2,es      ; vector
  424.     pop     es
  425.  
  426.     mov     dx,offset cs:async_isr          ; move relative offset of
  427.                                             ;   interrupt routine to dx
  428.     push    ds
  429.     push    cs                              ; move code segment address
  430.     pop     ds                              ;   to ds
  431.     mov     ah,25h                          ; DOS Set Vector function
  432.     int     21h                             ; call dos
  433.     pop     ds
  434.  
  435. ;* the only thing left to do is set up the various com port registers
  436.     mov     dx,com_port                     ; get port address
  437.     mov     al,DTR + OUT2 + RTS             ; set DTR, Out2, & RTS mask
  438.     add     dx,MODEM_CONTROL
  439.     out     dx,al                           ; set register
  440.     sub     dx,MODEM_CONTROL                ; point back to base
  441.  
  442. co4:
  443. ; loop until the UART's receive buffer is empty
  444.     add     dx,LINE_STATUS                  ; point to Line Status Register
  445.     in      al,dx                           ; read it
  446.     test    al,01h                          ; & check for a waiting char
  447.     jz      co5                             ; if no character continue
  448.                                             ; otherwise
  449.     sub     dx,LINE_STATUS                  ; point back to base
  450.     in      al,dx                           ; read the character
  451.     jmp     short co4                       ; and check it again
  452.  
  453. co5:
  454. ; set the UART to interrupt on received character or line status error
  455.     mov     int_enable_mask,DATA_AVAIL_INT + LINE_STATUS_INT
  456.     mov     dx,com_port                     ; get base address
  457.     add     dx,INT_ID                       ; point to Inter. ID Register
  458.     in      al,dx                           ; clear it
  459.     sub     dx,INT_ID
  460.     mov     al,int_enable_mask
  461.     add     dx,INT_ENABLE
  462.     out     dx,al                           ; enable UART interrupts
  463.  
  464.     or      com_status,PORT_OPEN            ; set status flag
  465.  
  466. ; now set up the Programable Interrupt Control (PIC) register
  467.     cli
  468.     in      al,PIC_MASK_REG                 ; read 8259 mask register
  469.     mov     ah,PIC_mask                     ; move mask to ah
  470.     not     ah                              ; flip the bits
  471.     and     al,ah                           ; reset mask for this port
  472.     out     PIC_MASK_REG,al
  473.     sti
  474.     mov     ax,OK
  475.     jmp     short co_out
  476.  
  477. co_err:
  478.     mov     ax,ERROR                        ; set zero flag and clear ax
  479. co_out:
  480.     pop     dx
  481.     pop     ds
  482.     pop     bx
  483.     pop     bp
  484.     ret
  485. _com_open   endp
  486.  
  487. ;**************************************************************************
  488. ;*  void    com_close(void);
  489. ;*
  490. ;*      com_close() restores the original interrupt vectors, disables the
  491. ;*  com interrupts, and restores the com registers.
  492. ;*
  493. _com_close  proc
  494.     push    bp
  495.     mov     bp,sp
  496.     push    ax
  497.     push    ds
  498.     push    dx
  499.  
  500.     if @datasize                            ; large data model
  501.         mov     ax,seg com_status           ; change to local data segment
  502.         mov     ds,ax                       ;   if necessary
  503.     endif
  504.     test    com_status,PORT_OPEN
  505.     jz      cc_out
  506.  
  507. ;* restore the 8259 PIC
  508.     cli
  509.     in      al,PIC_MASK_REG
  510.     xor     al,PIC_mask
  511.     out     PIC_MASK_REG,al
  512.     sti
  513.  
  514. ;* restore the UART registers
  515.     mov     al,00h                          ; turn off DTR, RTS, and OUT2
  516.     mov     dx,com_port
  517.     add     dx,MODEM_CONTROL
  518.     out     dx,al                           ; reset register
  519.     sub     dx,MODEM_CONTROL                ; point back to base
  520.     add     dx,INT_ENABLE                   ; and then to Inter. ID Register
  521.     out     dx,al                           ; and clear it
  522.  
  523. ;* restore the original com interrupt vector
  524.     push    ds
  525.     lds     dx,old_vector
  526.     mov     al,com_int
  527.     mov     ah,25h
  528.     int     21h
  529.     pop     ds
  530.  
  531.     mov     com_status,0                    ; clear the status word
  532.     mov     recv_tail,0                     ; and initialize the buffers
  533.     mov     send_tail,0
  534.  
  535. cc_out:
  536.     pop     dx
  537.     pop     ds
  538.     pop     ax
  539.     pop     bp
  540.     ret
  541. _com_close  endp
  542.  
  543. ;**************************************************************************
  544. ;*  int     com_write(int length,void *message);
  545. ;*
  546. ;*      This routine is passed the application program's send buffer and the
  547. ;*  the message length. The buffer address is saved and then the UART is set
  548. ;*  to interrupt whenever the Transmitter Holding Register is empty.
  549. ;*
  550. ;*  Returns:    -1 if error detected or already sending otherwise 0
  551. ;*
  552. _com_write proc
  553.     push    bp
  554.     mov     bp,sp
  555.     push    di
  556.     push    ds
  557.     push    dx
  558.     push    es
  559.     push    si
  560.  
  561.     if @datasize                            ; large data model
  562.         mov     ax,seg com_status           ; change to local data segment
  563.         mov     ds,ax                       ;   if necessary
  564.     endif
  565.     test    com_status,PORT_OPEN            ; see if port is open
  566.     jz      cw_err                          ; if not then return error
  567.                                             ; else
  568.     test    com_status,SNDNG_PCKT           ; see if we're already sending
  569.     jz      cw1                             ; if not continue
  570.                                             ; else
  571.     jmp     cw_err                          ; return error
  572.  
  573. cw1:
  574. ; copy the message into the local send buffer
  575.     mov     ax,[bp + bp_ofs]                ; get message length
  576.     mov     msg_len,ax                      ;   & save it
  577.     mov     cx,ax
  578.     les     di,send_buffer
  579.     mov     si,[bp + bp_ofs + 2]            ; get offset of send buffer
  580.     push    ds
  581.     if @datasize                            ; large data model
  582.         mov     ds,[bp + bp_ofs + 4]        ; get segment of send buffer
  583.     endif
  584.     cld
  585.     rep     movsb                           ; copy the message
  586.     pop        ds
  587.  
  588. ; now enable transmit interrupts
  589.     cli
  590.     or      int_enable_mask,TRANS_EMPTY_INT ; set mask
  591.     mov     dx,com_port                     ; put port address in dx
  592.     add     dx,INT_ENABLE                   ; Interrupt Enable Register
  593.     mov     al,int_enable_mask
  594.     out     dx,al                           ; enable interrupt
  595.     or      com_status,SNDNG_PCKT           ; set the status flag
  596.     sti
  597.     mov     ax,OK
  598.     jmp     short cw_out
  599.  
  600. cw_err:
  601.     mov     ax,ERROR                        ; set error return
  602.  
  603. cw_out:
  604.     pop     si
  605.     pop     es
  606.     pop     dx
  607.     pop     ds
  608.     pop     di
  609.     pop     bp
  610.     ret
  611. _com_write endp
  612.  
  613. ;*****************************************************************************
  614. ;*  int     com_read(void *recv_buf)
  615. ;*
  616. ;*      This routine is passed the application program's receive buffer.
  617. ;*  The contents (if any) of the local receive buffer are transferred to it.
  618. ;*  If hand-shaking is enabled then HAND_SHAKE is called and passed the
  619. ;*  BUF_EMPTY function code so that it can re-start receving if necessary.
  620. ;*
  621. ;*  Returns:    Length of received message or -1 if error.
  622. ;*
  623. _com_read   proc
  624.     push    bp
  625.     mov     bp,sp
  626.     push    cx
  627.     push    di
  628.     push    ds
  629.     push    es
  630.     push    si
  631.  
  632.     if @datasize                            ; large data model
  633.         mov     ax,seg com_status           ; change to local data segment
  634.         mov     ds,ax                       ;   if necessary
  635.     endif
  636.     push    ds
  637.     test    com_status,PORT_OPEN            ; make sure the port's open
  638.     jnz     cr1
  639.     jmp     cr_err
  640. cr1:
  641.     mov     di,[bp + bp_ofs]                ; get offset of program's buffer
  642.     if @datasize                            ; large data model
  643.         mov     ax,[bp + bp_ofs + 2]        ; get segment of program's buffer
  644.         mov     es,ax                       ;       & put it in es
  645.     else                                    ; else
  646.         mov     ax,ds
  647.         mov     es,ax
  648.     endif
  649.     lds     si,recv_buffer
  650.     mov     cx,recv_tail                    ; set cx to length of string recd
  651.     inc     cx
  652.     cld
  653.     rep     movsb                           ; copy the message
  654.  
  655. ; now re-initialize the buffer and flags
  656.     pop     ds
  657.     and     com_status,not RECVD_CHAR       ; reset status
  658.     and     com_status,not OVERFLOW
  659.     mov     cx,recv_tail                    ; return length of string
  660.     mov     recv_tail,0
  661.  
  662.     test    com_status,HAND_SHAKING         ; is hand-shaking is enabled?
  663.     jz      cr_2                            ; if not then exit
  664.                                             ; else
  665.     mov     ah,BUF_EMPTY                    ; set the function code
  666.     push    ax
  667.     if @codesize                            ; large or compact model
  668.         call    far ptr [hand_shake]
  669.     else                                    ; small or medium model
  670.         call    word ptr [hand_shake]
  671.     endif
  672.     add     sp,2                            ; adjust stack
  673.  
  674. cr_2:
  675.     mov     ax,cx
  676.     sti
  677.     jmp     short cr_out
  678.  
  679. cr_err:
  680.     mov     ax,ERROR                        ; set zero flag and clear ax
  681.  
  682. cr_out:
  683.     pop     si
  684.     pop     es
  685.     pop     ds
  686.     pop     di
  687.     pop     cx
  688.     pop     bp
  689.     ret
  690. _com_read   endp
  691.  
  692. ;**************************************************************************
  693. ;*  int com_read_char(void);
  694. ;*
  695. ;*      Returns the oldest character in the receive buffer. If there are
  696. ;*  no characters then an error is returned. Note that the return value is
  697. ;*  an integer. This means that a return value of 0 to 255 is the character
  698. ;*  received while -1 is an error.
  699. ;*
  700. ;*  Returns:    0 if successful or -1 if error occurs.
  701. ;*
  702. _com_read_char proc
  703.     push    bp
  704.     mov     bp,sp
  705.     push    cx
  706.     push    di
  707.     push    ds
  708.     push    es
  709.     push    si
  710.  
  711.     if @datasize                            ; large data model
  712.         mov     ax,seg com_status           ; change to local data segment
  713.         mov     ds,ax                       ;   if necessary
  714.     endif
  715.     test    com_status,PORT_OPEN            ; make sure port's open
  716.     jnz     crc1
  717.     jmp     crc_err
  718.  
  719. crc1:
  720.     cmp     recv_tail,0                     ; anything in the buffer?
  721.     je      crc_err                         ; no, so exit
  722.     mov     cx,recv_tail                    ; set cx for subsequent movsb
  723.     push    ds
  724.     lds     si,recv_buffer                  ; point ds:si to recv buffer
  725.     mov     al,[si]                         ; get 1st character in buffer
  726.     mov     ah,0                            ; clear high byte
  727.     inc     si                              ; point si to next character
  728.     les     di,recv_buffer                  ; es:di = beginning of buffer
  729.     cld
  730.     rep     movsb                           ; shift remaining characters down
  731.  
  732.     pop     ds
  733.     dec     recv_tail                       ; adjust tail
  734.     cmp     recv_tail,0                     ; check for empty buffer
  735.     jne     crc_out                         ; if not exit
  736.                                             ; else
  737.     and     com_status,not RECVD_CHAR       ; reset status
  738.     and     com_status,not OVERFLOW
  739.  
  740. crc_err:
  741.     mov     ax,ERROR                        ; set error flag and clear ax
  742.  
  743. crc_out:
  744.     pop     si
  745.     pop     es
  746.     pop     ds
  747.     pop     di
  748.     pop     cx
  749.     pop     bp
  750.     ret
  751. _com_read_char endp
  752.  
  753. ;**************************************************************************
  754. ;*  int com_write_char(char chr);
  755. ;*
  756. ;*      This function sends a character out the com port immediately.
  757. ;*  If a message is currently being sent an error code is returned.
  758. ;*
  759. _com_write_char proc
  760.     push    bp
  761.     mov     bp,sp
  762.     push    cx
  763.     push    di
  764.     push    ds
  765.     push    es
  766.     push    si
  767.  
  768.     if @datasize                            ; large data model
  769.         mov     ax,seg com_status           ; change to local data segment
  770.         mov     ds,ax                       ;   if necessary
  771.     endif
  772.     test    com_status,SNDNG_PCKT           ; see if we're sending
  773.     jnz     cwc_err                         ; if so, return an error
  774.                                             ; else
  775.     mov     dx,com_port                     ; get port base address
  776.     add     dx,LINE_STATUS                  ; point to Line Status Register
  777. cwc1:
  778. ; loop until the transmitter holding register is empty
  779.     in      al,dx
  780.     test    al,40h
  781.     jz      cwc1
  782.  
  783.     sub     dx,LINE_STATUS                  ; point back to base address
  784.     mov     al,[bp + bp_ofs]                ; get character to send
  785.     out     dx,al                           ; and send character
  786.     mov     ax,OK                           ; indicate no error
  787.     jmp     short cwc_out                   ; and exit
  788.  
  789. cwc_err:
  790.     mov     ax,ERROR
  791.  
  792. cwc_out:
  793.     pop     si
  794.     pop     es
  795.     pop     ds
  796.     pop     di
  797.     pop     cx
  798.     pop     bp
  799.     ret
  800. _com_write_char endp
  801.  
  802. ;**************************************************************************
  803. ;*  void    com_stop_sending(void);
  804. ;*
  805. ;*      Stop sending disables the the Transmit Buffer Empty interrupt.
  806. ;*
  807. _com_stop_sending proc
  808.     push    ax
  809.     push    ds
  810.     push    dx
  811.  
  812.     if @datasize                            ; large data model
  813.         mov     ax,seg com_status
  814.         mov     ds,ax
  815.     endif
  816.     test    com_status,SNDNG_PCKT           ; are we in send mode?
  817.     jz      css1_out                        ; no, so just return
  818.     cli                                     ; make sure we're not interrupted
  819.     and     int_enable_mask,not TRANS_EMPTY_INT ; reset interrupt status
  820.     mov     dx,com_port                     ; re-set Int. Enable Reg.
  821.     add     dx,INT_ENABLE
  822.     mov     al,int_enable_mask              ; disable interrupt
  823.     out     dx,al
  824.     sti
  825.  
  826. css1_out:
  827.     pop     dx
  828.     pop     ds
  829.     pop     ax
  830.     ret
  831. _com_stop_sending endp
  832.  
  833. ;**************************************************************************
  834. ;*  void    com_start_sending(void);
  835. ;*
  836. ;*      Start sending is the complement to stop sending. It checks to see
  837. ;*  if the SNDNG_PCKT flag is true and if so re-enables the transmit
  838. ;*  interrupt. Ohterwise it simply returns.
  839. ;*
  840. _com_start_sending proc
  841.     push    bp
  842.     mov     bp,sp
  843.     push    ax
  844.     push    ds
  845.     push    dx
  846.  
  847.     if @datasize                            ; large data model
  848.         mov     ax,seg com_status
  849.         mov     ds,ax
  850.     endif
  851.     test    com_status,SNDNG_PCKT           ; are we in send mode?
  852.     jz      css2_out                        ; no, so just return
  853.     cli                                     ; make sure we're not interrupted
  854.     or      int_enable_mask,TRANS_EMPTY_INT ; set status to send interrupts
  855.     mov     dx,com_port                     ; put port address in dx
  856.     add     dx,INT_ENABLE                   ; Interrupt Enable Register
  857.     mov     al,int_enable_mask
  858.     out     dx,al                           ; enable  register
  859.     sti
  860.  
  861. css2_out:
  862.     pop     dx
  863.     pop     ds
  864.     pop     ax
  865.     pop     bp
  866.     ret
  867. _com_start_sending endp
  868.  
  869. ;**************************************************************************
  870. ;*  unsigned int    com_get_status(void);
  871. ;*
  872. ;*      This function returns a copy of the com_status word maintained
  873. ;*  by this module. The status word consists of a sset of bit flags. See
  874. ;*  SERIAL.HPP for the bit structure that defines them.
  875. ;*
  876. _com_get_status proc
  877.     push    ds
  878.  
  879.     if @datasize                            ; large data model
  880.         mov     ax,seg com_status
  881.         mov     ds,ax
  882.     endif
  883.     mov     ax,com_status
  884.  
  885.     pop     ds
  886.     ret
  887. _com_get_status endp
  888.  
  889. ;**************************************************************************
  890. ;*  int     com_chars_recvd(void);
  891. ;*
  892. ;*      Returns the number of characters currently in the receive buffer.
  893. ;*
  894. _com_chars_recvd proc
  895.     push    ds
  896.  
  897.     if @datasize                            ; large data model
  898.         mov     ax,seg com_status
  899.         mov     ds,ax
  900.     endif
  901.     mov     ax,recv_tail
  902.  
  903.     pop     ds
  904.     ret
  905. _com_chars_recvd endp
  906.  
  907. ;**************************************************************************
  908. ;*  int     com_chars_sent
  909. ;*
  910. ;*      Returns the number of characters that have been sent.
  911. ;*
  912. _com_chars_sent proc
  913.     push    ds
  914.  
  915.     if @datasize                            ; large data model
  916.         mov     ax,seg com_status
  917.         mov     ds,ax
  918.     endif
  919.     mov     ax,send_tail
  920.  
  921.     pop     ds
  922.     ret
  923. _com_chars_sent endp
  924.  
  925. ;**************************************************************************
  926. ;*  void    com_set_buffers(void *recv_buffer, void *send_buffer, int len);
  927. ;*
  928. ;*      Sets the local send and receive buffers to be used by this module.
  929. ;*
  930. _com_set_buffers proc
  931.     push    bp
  932.     mov     bp,sp
  933.     push    ax
  934.     push    ds
  935.  
  936.     if @datasize                            ; large data model
  937.         mov     ax,seg com_status           ; change to local data segment
  938.         mov     ds,ax                       ;   if necessary
  939.         mov     ax,[bp + bp_ofs]            ; receive buffer offset
  940.         mov     word ptr recv_buffer,ax
  941.         mov     ax,[bp + bp_ofs + 2]        ; receive buffer segment
  942.         mov     word ptr recv_buffer + 2,ax
  943.         mov     ax,[bp + bp_ofs + 4]        ; send buffer offset
  944.         mov     word ptr send_buffer,ax
  945.         mov     ax,[bp + bp_ofs + 6]        ; send buffer segment
  946.         mov     word ptr send_buffer + 2,ax
  947.         mov     ax,[bp + bp_ofs + 8]        ; buffer length
  948.     else
  949.         mov     ax,[bp + bp_ofs]            ; receive buffer offset
  950.         mov     word ptr recv_buffer,ax
  951.         mov     word ptr recv_buffer + 2,ds
  952.         mov     ax,[bp + bp_ofs + 2]        ; send buffer offset
  953.         mov     word ptr send_buffer,ax
  954.         mov     word ptr send_buffer + 2,ds
  955.         mov     ax,[bp + bp_ofs + 4]        ; send buffer length
  956.     endif
  957.     mov     send_len,ax
  958.     mov     recv_len,ax
  959.  
  960.     pop     ds
  961.     pop     ax
  962.     pop     bp
  963.     ret
  964. _com_set_buffers endp
  965.  
  966. ;**************************************************************************
  967. ;*  void    com_clr_recv_buf(void);
  968. ;*
  969. ;*      Effectively clears all characters from the receive buffer by
  970. ;*  resetting recv_tail to 0.
  971. ;*
  972. _com_clr_recv_buf proc
  973.     push    ds
  974.  
  975.     if @datasize                            ; large data model
  976.         mov     ax,seg com_status
  977.         mov     ds,ax
  978.     endif
  979.     cli
  980.     mov     recv_tail,0
  981.     sti
  982.  
  983.     pop     ds
  984. _com_clr_recv_buf endp
  985.  
  986. ;**************************************************************************
  987. ;*  void    com_clr_recv_buf(void);
  988. ;*
  989. ;*      Effectively clears all characters from the send buffer by
  990. ;*  resetting send_tail and msg_len to 0. Note: if we're currently
  991. ;*  sending then sending is stopped.
  992. ;*
  993. _com_clr_send_buf proc
  994.     push    ds
  995.  
  996.     if @datasize                            ; large data model
  997.         mov     ax,seg com_status
  998.         mov     ds,ax
  999.     endif
  1000.     test    com_status,SNDNG_PCKT           ; are we sending now?
  1001.     jz      ccsb1                           ; no, continue
  1002.                                             ; else
  1003.     call    _com_stop_sending               ; stop sending
  1004.  
  1005. ccsb1:
  1006.     cli                                     ; avoid interruptions
  1007.     and     com_status,not SNDNG_PCKT
  1008.     mov     word ptr send_tail,0
  1009.     mov     word ptr msg_len,0
  1010.     sti
  1011.  
  1012.     pop     ds
  1013. _com_clr_send_buf endp
  1014.  
  1015.  
  1016. ;**************************************************************************
  1017. ;*  void    _com_set_handshake(HANDSHAKE hs);
  1018. ;*
  1019. ;*      Sets a pointer to the hand-shaking routine and sets the status
  1020. ;*  flag indicating that hand-shaking is enabled. If the pointer passed
  1021. ;*  to this routine is NULL then hand-shaking is disabled.
  1022. ;*
  1023. _com_set_handshake proc near
  1024.     push    bp
  1025.     mov     bp,sp
  1026.     push    ax
  1027.     push    bx
  1028.     push    ds
  1029.  
  1030.     if @datasize                            ; large data model
  1031.         mov     ax,seg com_status           ; change to local data segment
  1032.         mov     ds,ax                       ;   if necessary
  1033.     endif
  1034.  
  1035.     mov     ax,[bp + bp_ofs]                ; get offset address of hand-
  1036.                                             ;   shaking function
  1037.     mov     word ptr hand_shake,ax          ; save it
  1038.     if @codesize                            ; large or compact model
  1039.         mov     bx,[bp + bp_ofs + 2]        ; get segment address
  1040.         or      ax,bx                       ; see if we were passed a NULL
  1041.         jz      csh1                        ; if so, disable hand-shaking
  1042.                                             ; else
  1043.         mov     word ptr hand_shake + 2,bx  ; save the segment
  1044.         or      com_status,HAND_SHAKING     ; set the enable flag
  1045.         jmp     short csh_out
  1046.     else                                    ; small or medium model
  1047.         test    ax,0ffffh                   ; were we passed a NULL?
  1048.         jz      csh1                        ; yes, so disable hand-shaking
  1049.                                             ; else
  1050.         mov     word ptr hand_shake + 2,cs  ; set hand-shake segment to cs
  1051.         or      com_status,HAND_SHAKING     ; set the enable flag
  1052.         jmp     short csh_out
  1053.     endif
  1054.  
  1055. csh1:
  1056.     mov     word ptr hand_shake + 2,0
  1057.     and     com_status,not HAND_SHAKING     ; disable hand-shaking
  1058.  
  1059. csh_out:
  1060.     pop     ds
  1061.     pop     bx
  1062.     pop     ax
  1063.     pop     bp
  1064.     ret
  1065. _com_set_handshake endp
  1066.  
  1067.  
  1068.     end
  1069.